﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Reflection;
using System.Windows.Forms;
using Microsoft.Office.Core;
using Microsoft.Office.Interop.PowerPoint;
using Shape = Microsoft.Office.Interop.PowerPoint.Shape;

namespace Microsoft.Office.PowerPoint.STAMP.Core
{
    /// <summary>
    /// Powerpoint object utilities.
    /// </summary>
    public class UtilPowerPoint
    {
        /// <summary>
        /// Creates a temp presentation from the current active slide.
        /// </summary>
        /// <param name="tempPresentation">Temp presentation that holds the slide.</param>
        /// <param name="slide">Slide thats added to the temp presentation.</param>
        /// <param name="tempPresentationFile">File name of previous file.</param>
        /// <returns>Returns temp presentation filename.</returns>
        public static string CreateTempPresentation(Presentation tempPresentation, Slide slide, string tempPresentationFile)
        {
            if(!String.IsNullOrEmpty(tempPresentationFile) && File.Exists(tempPresentationFile))
            {
                return tempPresentationFile;
            }

            string filename = Path.GetTempFileName();
            
            slide.Copy();
            tempPresentation.Slides.Paste(1);
            tempPresentation.SaveAs(filename, PpSaveAsFileType.ppSaveAsOpenXMLPresentation, MsoTriState.msoFalse);
            tempPresentation.Close();

            return filename;
        }

        /// <summary>
        /// Traverses throught the InteractiveSequences to get to each shapes effects,
        /// then deletes the animation based on the trigger type
        /// </summary>
        /// <param name="slide">The slide that host the shape</param>
        /// <param name="shape">The shape that contains the effects to be deleted and checking that the name matches eg textbox with fade in effect</param>
        /// <param name="triggerType">The trigger type used to check that the right effect is deleted</param>
        public static void DeleteShapeAnimationsByTriggerType(Slide slide, Shape shape, MsoAnimTriggerType triggerType, IList<string> shapeNames)
        {
            for (int i = slide.TimeLine.InteractiveSequences.Count; i >= 1; i--)
            {
                Sequence sequence = slide.TimeLine.InteractiveSequences[i];
                for (int x = sequence.Count; x >= 1; x--)
                {
                    Effect effect = sequence[x];
                    if (effect.Timing.TriggerType == triggerType)
                    {
                        if(shapeNames.Contains(effect.Shape.Name))
                            effect.Delete();
                    }
                }
            }
        }

        /// <summary>
        ///  Counts all the effects on the slide which is of trigger type
        /// </summary>
        /// <param name="slide">The slide that contains the animations to be counted</param>
        /// <param name="triggerType">The trigger type being counted</param>
        /// <returns></returns>
        public static int CountAnimationsByTriggerType(Slide slide, MsoAnimTriggerType triggerType)
        {
            int animationsCount = 0;
            for (int i = slide.TimeLine.InteractiveSequences.Count; i >= 1; i--)
            {
                Sequence sequence = slide.TimeLine.InteractiveSequences[i];
                for (int x = sequence.Count; x >= 1; x--)
                {
                    Effect effect = sequence[x];
                    if (effect.Timing.TriggerType == triggerType)
                    {
                        animationsCount++;
                    }
                }
            }
            return animationsCount;
        }


        /// <summary>
        /// Adds the bookmark details to the media shape
        /// </summary>
        /// <param name="mediaShape">Media shape to which the bookmark is added.</param>
        /// <param name="position">Position which the booke mark is associated.</param>
        /// <param name="name">Unique name given to the bookmark to that it can be referenced.</param>
        public static void AddMediaBookmark(Shape mediaShape, int position, string name)
        {
            mediaShape.MediaFormat.MediaBookmarks.Add(position, name);
        }

        /// <summary>
        /// Main method to add the animation to the slide
        /// </summary>
        /// <param name="slide">Slide that hosts the shape that will have the animation added to the timeline.</param>
        /// <param name="shape">Shape that animation is associated with. eg textbox</param>
        /// <param name="triggerShape">Trigger Shape that animation is linked to start. eg video (media object)</param>
        /// <param name="bookmarkName">The bookmark name which will start the animation.</param>
        /// <param name="exit">Sets the effect exit property when the animation has to fade exit.</param>
        private static void AddAnimation(Slide slide, Shape shape, Shape triggerShape, string bookmarkName, bool exit)
        {
            Effect effect = slide.TimeLine.InteractiveSequences.Add().AddTriggerEffect(
                shape,
                MsoAnimEffect.msoAnimEffectFade,
                MsoAnimTriggerType.msoAnimTriggerOnMediaBookmark,
                triggerShape,
                bookmarkName,
                MsoAnimateByLevel.msoAnimateLevelNone);
            effect.Timing.TriggerDelayTime = 0.1f;

            if (exit)
            {
                effect.Exit = MsoTriState.msoTrue;
            }
        }

        /// <summary>
        /// Adds a 'fade' entrance animation to the shape based on an existing bookmark
        /// </summary>
        /// <param name="slide">Slide that hosts the shape that will have the animation added to the timeline.</param>
        /// <param name="shape">Shape that animation is associated with. eg textbox</param>
        /// <param name="triggerShape">Trigger Shape that animation is linked to. eg video (media object)</param>
        /// <param name="bookmarkName">The bookmark name which will start the animation </param>
        public static void AddEntranceAnimation(Slide slide, Shape shape, Shape triggerShape, string bookmarkName)
        {
            AddAnimation(slide, shape, triggerShape, bookmarkName, false);
        }

        /// <summary>
        /// Adds a ‘fade’ exit animation to a shape based on an existing bookmark.
        /// </summary>
        /// <param name="slide">Slide that hosts the shape that will have the animation added to the timeline.</param>
        /// <param name="shape">Shape that animation is associated with. eg textbox</param>
        /// <param name="triggerShape">Trigger Shape that animation is linked to. eg video (media object)</param>
        /// <param name="bookmarkName">The bookmark name which will start the animation </param>
        public static void AddExitAnimation(Slide slide, Shape shape, Shape triggerShape, string bookmarkName)
        {
            AddAnimation(slide, shape, triggerShape, bookmarkName, true);
        }

        /// <summary>
        /// Loops throught the MediaBookmarks and ONLY deletes the specific bookmark where the bookmark name is Equal
        /// </summary>
        /// <param name="mediaShape">Media shape that contains the bookmarks to be deleted.</param>
        /// <param name="bookMarkName">The bookmark to be deleted.</param>
        public static void DeleteBookmarkByName(Shape mediaShape, string bookMarkName)
        {
            for (int i = mediaShape.MediaFormat.MediaBookmarks.Count; i >= 1; i--)
            {
                if (mediaShape.MediaFormat.MediaBookmarks[i].Name.Equals(bookMarkName))
                {
                    mediaShape.MediaFormat.MediaBookmarks[i].Delete();
                    break;
                }
            }
        }

        /// <summary>
        ///  Loops throught the MediaBookmarks collection and deletes ALL the bookmarks where the bookmark name matches the mediaShapeName
        /// </summary>
        /// <param name="mediaShape">Media shape that contains the bookmarks to be deleted.</param>
        /// <param name="mediaShapeName">Media name to compare to when deleting the bookmarks.</param>
        public static void DeleteBookmarks(Shape mediaShape, string mediaShapeName)
        {
            for (int i = mediaShape.MediaFormat.MediaBookmarks.Count; i >= 1; i--)
            {
                if (ShapeFinder.MatchName(mediaShape.MediaFormat.MediaBookmarks[i].Name, mediaShapeName))
                    mediaShape.MediaFormat.MediaBookmarks[i].Delete();
            }
        }

        /// <summary>
        /// Sets the txtbox properties pertaining to the caption.
        /// </summary>
        public static void SetTextboxCaptionDetails(Shape textbox, CaptionItem item, string mediaId, string truncatedText)
        {
            var height = textbox.Height;
            textbox.Name = item.Name;
            textbox.TextFrame.TextRange.Text = truncatedText;

            SetShapeTag(textbox, Constants.CaptionMediaId, mediaId);
            SetShapeTag(textbox, Constants.CaptionInternalId, item.InternalId);
            SetShapeTag(textbox, Constants.CaptionItemSysName, item.Name);
            SetShapeTag(textbox, Constants.BookMarkEntryName, item.BookMarkEntryName);
            SetShapeTag(textbox, Constants.BookMarkExitName, item.BookMarkExitName);
            SetShapeTag(textbox, Constants.CaptionDisplayArea, item.IsCaptionDisplayArea.ToString());
        }

        /// <summary>
        /// Adds the textbox to the slide setting the position
        /// </summary>
        /// <param name="slide">The slide that will contain the textbox.</param>
        /// <param name="left">The left positon of the textbox on the slide.</param>
        /// <param name="top">The top positon of the textbox on the slide.</param>
        /// <param name="width">The width positon of the textbox on the slide.</param>
        /// <param name="height">The height positon of the textbox on the slide.</param>
        /// <returns>Returns the shape to the calling method so it can be used further.</returns>
        public static Shape AddTextboxShape(Slide slide, float left, float top, float width, float height)
        {
            Shape shape = slide.Shapes.AddTextbox(MsoTextOrientation.msoTextOrientationHorizontal, left, top, width, height);
            shape.TextFrame.AutoSize = PpAutoSize.ppAutoSizeNone;

            return shape;
        }

        /// <summary>
        /// Adds a Tag and Value to an Shape
        /// </summary>
        /// <param name="shape">Shape needs tag added.</param>
        /// <param name="tag">Tag to be added to the Shape.</param>
        /// <param name="value">Value set for the Tag.</param>
        public static void SetShapeTag(Shape shape, string tag, string value)
        {
            shape.Tags.Add(tag, value);
        }

        /// <summary>
        /// Gets the tag value for the shape.
        /// </summary>
        /// <param name="shape">The Shape.</param>
        /// <param name="tag">Tag needed.</param>
        /// <returns>Returns the value contained in the tag</returns>
        public static string GetShapeTagValue(Shape shape, string tag)
        {
            if (string.IsNullOrEmpty(shape.Tags[tag]))
                return null;

            return shape.Tags[tag];
        }

        /// <summary>
        /// Stores the XML string representation of Captions inside the presentation
        /// </summary>
        /// <param name="presentation">Presentation the XML is stored in</param>
        /// <param name="xml">XML string to be stored</param>
        /// <returns>Returns the Id of the stored XML</returns>
        public static string AddCustomXmlPartToPresentation(Presentation presentation, string xml)
        {
            CustomXMLPart xmlPart = presentation.CustomXMLParts.Add(xml);

            return xmlPart.Id;
        }

        /// <summary>
        /// Gets the stored XML string value of the Caption objects from the presentation
        /// </summary>
        /// <param name="presentation">Presentation the XML is stored in</param>
        /// <param name="customXMLPartId">Id used for retreiving the string XML</param>
        /// <returns>string XML representation</returns>
        public static string GetCustomXmlPartToPresentationById(Presentation presentation, string customXMLPartId)
        {
            return presentation.CustomXMLParts.SelectByID(customXMLPartId).XML;
        }

        /// <summary>
        /// Deletes the stored XML from the presentation
        /// </summary>
        /// <param name="presentation">Presentation the XML is stored in</param>
        /// <param name="customXMLPartId">Id used for deleting the stored string XML</param>
        public static void DeleteCustomXmlPartToPresentationById(Presentation presentation, string customXMLPartId)
        {
            presentation.CustomXMLParts.SelectByID(customXMLPartId).Delete();
        }
    }
}